package asteroid

import (
	"fmt"
	"runtime/debug"
	"strings"
	"sync"
	"sync/atomic"

	"gddgame.cc/galaxy/utils/def"
)

//go:generate msgp

type MessageType byte

const (
	_ MessageType = iota

	RegionMessage
	ChannelMessage
	ProxyMessage
	EnterMessage
	ExitMessage
)

type MsgType uint8

// Room
const (
	_ MsgType = iota
	OpenRoomMessageType
	ConnectRoomMessageType
	RequestRoomMessageType
	ResponseRoomMessageType
	BroadcastRoomMessageType
	CloseRoomMessageType
)

// Channel
const (
	_ MsgType = iota
	RequestChannelMessageType
	ResponseChannelMessageType
)

// Proxy
const (
	_ MsgType = iota
	ForwardProxyMessageType
	FindProxyMessageType
)

var (
	msgPool = sync.Pool{}
)

func init() {
	msgPool.New = newMsgInf
}

func newMsgInf() interface{} {
	msg := &Msg{alloc: 0, b: make([]byte, 0, 1024), releaseList: []def.AutoSerializer{}, responseReleaseList: []def.AutoSerializer{}}
	msg.message = &message{
		Upstream: msg,
		bag:      Bag{b: make([]byte, 0, 1024)},
	}
	return msg
}

type msgMessage interface {
	Message
	release()
	set(data interface{}, response bool)
	get() ([]byte, error)
}

//msgp:tuple Msg
type Msg struct {
	// Layer层逻辑
	MessageType MessageType `msg:"message"`
	// 逻辑分层：region，proxy
	Logic string  `msg:"logic"`
	Type  MsgType `msg:"type"`
	// 回复id
	Rid int `msg:"rid"`
	// 发送节点
	From string `msg:"from"`
	// 业务关系
	Key string `msg:"key"`
	// api接口
	Event interface{} `msg:"event"`
	// 请求来源：socket，session等
	Origin string `msg:"origin"`
	// 请求目标：socket，server
	Target string `msg:"target"`
	// 数据载荷
	PayloadData []byte `msg:"payload"`
	// 来源代理
	Proxy string `msg:"proxy"`

	stream  def.Stream
	message msgMessage

	cross interface{}
	layer *Layer
	b     []byte

	responseAction

	releaseList []def.AutoSerializer

	responseReleaseList []def.AutoSerializer

	to    []string
	alloc int32
}

func newMsg(t MsgType, logic string, key string) *Msg {
	m := msgPool.Get().(*Msg)
	m.alloc = 0
	m.Origin = ""
	m.From = ""
	m.Event = ""
	m.Target = ""
	m.PayloadData = nil
	m.Proxy = ""

	m.to = nil
	m.Rid = 0
	m.responseAction = nil
	m.Type = t
	m.Logic = logic
	m.Key = key

	m.cross = nil
	m.layer = nil
	return m
}

func newAccept(msgB []byte, layer *Layer) (*Msg, error) {
	msg := newMsg(0, "", "")
	_, err := msg.UnmarshalMsg(msgB)
	if err != nil {
		msg.Release()
		return nil, err
	}
	msg.layer = layer
	// 判断是否是本地网关
	// response会再次校验
	if msg.Proxy != "" && layer.worker.IsLocal(msg.Proxy) {
		msg.Proxy = ""
	}
	return msg, nil
}

func (msg *Msg) Release() {
	if msg.alloc < 0 {
		fmt.Println("release", msg.alloc, msg.Key, msg.Event, msg.to)
		debug.PrintStack()
	}
	//debug.PrintStack()
	if msg.alloc > 0 {
		atomic.AddInt32(&msg.alloc, -1)
		return
	}
	if msg.alloc == -1 {
		return
	}
	if msg.stream != nil {
		msg.stream.Release()
		msg.stream = nil
	}
	msg.message.release()
	if msg.releaseList != nil {
		for _, re := range msg.releaseList {
			if re != nil {
				re.Release()
			}
		}
		msg.releaseList = msg.releaseList[0:0]
	}
	if msg.responseReleaseList != nil {
		for _, re := range msg.responseReleaseList {
			if re != nil {
				re.Release()
			}
		}
		msg.responseReleaseList = msg.responseReleaseList[0:0]
	}
	msg.PayloadData = nil
	msg.alloc = -1
	msgPool.Put(msg)
}

func (msg *Msg) Alloc() {
	atomic.AddInt32(&msg.alloc, 1)
}

func (msg *Msg) Pipe(pipe *Pipe, manager *responseManager) {
	//fmt.Println("pipe", pipe, manager)
	//debug.PrintStack()
	msg.Origin = pipe.origin
	msg.Target = pipe.target
	msg.to = pipe.nodes
	msg.Event = pipe.event
	msg.message.set(pipe.data, false)

	msg.layer = pipe.layer
	msg.cross = pipe.cross

	msg.releaseList = pipe.releaseList

	if pipe.response != nil && manager != nil {
		// 需要response
		count := len(msg.to)
		if msg.cross != nil {
			// -1为支持跨域通信，不判断响应节点数
			count = -1
		}
		msg.Rid = manager.addResponse(count, pipe.response)
	}
}

func (msg *Msg) Send(messageType MessageType, local func(msg *Msg) bool) error {
	var err error
	msg.MessageType = messageType
	if msg.cross != nil {
		msg.Proxy = msg.layer.worker.Wan()

		switch t := msg.cross.(type) {
		case string:
			msg.Alloc()
			if strings.Contains(t, ":") {
				// 目标网关: 一般用于回发
				if err = msg.layer.forwards(t, msg.to, msg); err != nil {
					goto Release
				}
			} else {
				// 只可能是单个服务，所有逻辑层
				if err = msg.layer.forward("", t, msg); err != nil {
					goto Release
				}
			}
		case map[string]string:
			for logic, key := range t {
				msg.Alloc()
				if err = msg.layer.forward(logic, key, msg); err != nil {
					goto Release
				}
			}
		case map[string][]string:
			for logic, keys := range t {
				msg.Alloc()
				if err = msg.layer.forwards(logic, keys, msg); err != nil {
					goto Release
				}
			}
		}
	} else {
		for _, node := range msg.to {
			msg.Alloc()

			//if msg.Key == "core" {
			//	fmt.Println("core node", node)
			//}
			if local != nil && node == msg.layer.node {
				//fmt.Println("local", msg)
				local(msg)
				msg.Release()
			} else if msg.layer.socket != nil {
				c := msg.layer.socket.Get(node)
				//if msg.Key == "core" {
				//}
				if c != nil {
					c <- msg
				}
			} else {
				//if msg.Key == "core" {
				//	fmt.Println("core miss")
				//}
			}
		}
	}
Release:
	msg.Release()
	return err
}

func (msg *Msg) BindRequest(req *Msg, data interface{}) {
	msg.Rid = req.Rid
	msg.layer = req.layer
	msg.message.set(data, true)
	var node string
	node = msg.layer.backward(req, msg)
	if !msg.layer.socket.IsConnect(node) {
		_ = msg.layer.ConnectNode(node)
	}
	msg.releaseList = req.responseReleaseList
	req.responseReleaseList = req.responseReleaseList[0:0]

}

func (msg *Msg) Payload() ([]byte, error) {
	var err error
	if msg.PayloadData == nil {
		if msg.stream != nil {
			msg.PayloadData, err = msg.stream.Payload()
			if err != nil {
				return nil, err
			}
		} else {
			msg.PayloadData, err = msg.message.get()
			if err != nil {
				return nil, err
			}
			//fmt.Println("packet", msg, msg.message.bag.Data)
		}
	}
	return msg.MarshalMsg(msg.b)
}

func (msg *Msg) GetPayload() []byte {
	return msg.PayloadData
}
func (msg *Msg) GetFrom() string {
	return msg.From
}
func (msg *Msg) GetOrigin() string {
	return msg.Origin
}
func (msg *Msg) GetTarget() string {
	return msg.Target
}
func (msg *Msg) GetEvent() interface{} {
	return msg.Event
}
func (msg *Msg) Response(data interface{}, serializer ...def.AutoSerializer) {
	if msg.responseAction != nil {
		for _, re := range serializer {
			if re != nil {
				re.Alloc()
				msg.responseReleaseList = append(msg.responseReleaseList, re)
			}
		}
		msg.responseAction.response(msg, data)
	}
}
func (msg *Msg) IsCross() bool {
	return msg.Proxy != ""
}
func (msg *Msg) Cross() string {
	return msg.Proxy
}

//msgp ISlice
type ISlice []interface{}
